home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / hplip / fax / fax.py < prev    next >
Text File  |  2009-10-09  |  31KB  |  951 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. from __future__ import generators
  23.  
  24. # Std Lib
  25. import sys
  26. import os
  27. import threading
  28. import cPickle
  29. import time
  30. from cStringIO import StringIO
  31. import struct
  32.  
  33. # Local
  34. from base.g import *
  35. from base.codes import *
  36. from base.ldif import LDIFParser
  37. from base import device, utils, vcard
  38. from prnt import cups
  39.  
  40. try:
  41.     import coverpages
  42. except ImportError:
  43.     pass
  44.  
  45. try:
  46.     import dbus
  47. except ImportError:
  48.     log.error("dbus is required for PC send fax.")
  49.  
  50. import warnings
  51. # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
  52. # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
  53. warnings.simplefilter("ignore", DeprecationWarning)
  54.  
  55.  
  56. # Update queue values (Send thread ==> UI)
  57. STATUS_IDLE = 0
  58. STATUS_PROCESSING_FILES = 1
  59. STATUS_SENDING_TO_RECIPIENT = 2
  60. STATUS_DIALING = 3
  61. STATUS_CONNECTING = 4
  62. STATUS_SENDING = 5
  63. STATUS_COMPLETED = 6
  64. STATUS_CREATING_COVER_PAGE = 7
  65. STATUS_ERROR = 8
  66. STATUS_BUSY = 9
  67. STATUS_CLEANUP = 10
  68.  
  69. # Event queue values (UI ==> Send thread)
  70. EVENT_FAX_SEND_CANCELED = 1
  71. # Other values in queue are:
  72. #EVENT_FAX_RENDER_COMPLETE_BEGIN = 8010
  73. #EVENT_FAX_RENDER_COMPLETE_SENDDATA = 8011
  74. #EVENT_FAX_RENDER_COMPLETE_END = 8012
  75.  
  76. # **************************************************************************** #
  77. # HPLIP G3 Fax File Format (big endian)
  78. #
  79. # #==============================================#
  80. # # File Header: Total 28 bytes                  #
  81. # #..............................................#
  82. # # Magic bytes: 8 bytes ("hplip_g3")            #
  83. # # Format version: 8 bits (1)                   #
  84. # # Total pages in file(=p): 32 bits             #
  85. # # Hort DPI: 16 bits (200 or 300)               #
  86. # # Vert DPI: 16 bits (100, 200, or 300)         #
  87. # # Page Size: 8 bits (0=Unk, 1=Letter, 2=A4,    #
  88. # #                    3=Legal)                  #
  89. # # Resolution: 8 bits (0=Unk, 1=Std, 2=Fine,    #
  90. # #                     3=300DPI)                #
  91. # # Encoding: 8 bits (2=MH, 4=MMR, 7=JPEG)       #
  92. # # Reserved1: 32 bits (0)                       #
  93. # # Reserved2: 32 bits (0)                       #
  94. # #----------------------------------------------#
  95. # # Page 1 Header: Total 24 bytes                #
  96. # #..............................................#
  97. # # Page number: 32 bits (1 based)               #
  98. # # Pixels per row: 32 bits                      #
  99. # # Rows this page: 32 bits                      #
  100. # # Image bytes this page(=x): 32 bits           #
  101. # # Thumbnail bytes this page(=y): 32 bits       #
  102. # #  (thumbnail not present if y == 0)           #
  103. # #  (encoding?)                                 #
  104. # #     letter: 134 px wide x 173 px high        #
  105. # #     legal:  134 px wide x 221 px high        #
  106. # #     a4 :    134 px wide x 190 px high        #
  107. # # Reserved3: 32 bits (0)                       #
  108. # #..............................................#
  109. # # Image data: x bytes                          #
  110. # #..............................................#
  111. # # Thumbnail data: y bytes (if present)         #
  112. # #----------------------------------------------#
  113. # # Page 2 Header: Total 24 bytes                #
  114. # #..............................................#
  115. # # Image Data                                   #
  116. # #..............................................#
  117. # # Thumbnail data (if present)                  #
  118. # #----------------------------------------------#
  119. # # ... Pages 3 - (p-1) ...                      #
  120. # #----------------------------------------------#
  121. # # Page p Header: Total 24 bytes                #
  122. # #..............................................#
  123. # # Image Data                                   #
  124. # #..............................................#
  125. # # Thumbnail data (if present)                  #
  126. # #==============================================#
  127. #
  128.  
  129. RESOLUTION_STD = 1
  130. RESOLUTION_FINE = 2
  131. RESOLUTION_300DPI = 3
  132.  
  133. FILE_HEADER_SIZE = 28
  134. PAGE_HEADER_SIZE = 24
  135.  
  136. # **************************************************************************** #
  137.  
  138. ##skip_dn = ["uid=foo,ou=People,dc=example,dc=com",
  139. ##    "uid=bar,ou=People,dc=example,dc=com", "dc=example,dc=com"]
  140.  
  141. class FaxLDIFParser(LDIFParser):
  142.     def __init__(self, input, db):
  143.         LDIFParser.__init__(self, input)
  144.         self.db = db
  145.  
  146.     def handle(self, dn, entry):
  147.         if dn:
  148.             try:
  149.                 firstname = entry['givenName'][0]
  150.             except KeyError:
  151.                 try:
  152.                     firstname = entry['givenname'][0]
  153.                 except KeyError:
  154.                     firstname = ''
  155.  
  156.             try:
  157.                 lastname = entry['sn'][0]
  158.             except KeyError:
  159.                 lastname = ''
  160.  
  161.             try:
  162.                 nickname = entry['cn'][0]
  163.             except KeyError:
  164.                 nickname = firstname + ' ' + lastname
  165.  
  166.             try:
  167.                 fax = entry['facsimiletelephonenumber'][0] # fax
  168.             except KeyError:
  169.                 try:
  170.                     fax = entry['fax'][0]
  171.                 except KeyError:
  172.                     fax  = ''
  173.  
  174.             grps = []
  175.             try:
  176.                 grps = entry['ou']
  177.             except KeyError:
  178.                 pass
  179.  
  180.             grps.append(u'All')
  181.             groups = [g for g in grps if g]
  182.  
  183.             if nickname:
  184.                 log.debug("Import: name=%s, fax=%s, group(s)=%s, notes=%s" % ( nickname, fax, ','.join(groups), dn))
  185.                 self.db.set(nickname, title, firstname, lastname, fax, groups, dn)
  186.  
  187.  
  188.  
  189. # **************************************************************************** #
  190. class FaxAddressBook(object): # Pickle based address book
  191.     def __init__(self):
  192.         self._data = {}
  193.         #
  194.         # { 'name' : {'name': u'',
  195.         #             'firstname' : u'', # NOT USED STARTING IN 2.8.9
  196.         #             'lastname': u', # NOT USED STARTING IN 2.8.9
  197.         #             'title' : u'',  # NOT USED STARTING IN 2.8.9
  198.         #             'fax': u'',
  199.         #             'groups' : [u'', u'', ...],
  200.         #             'notes' : u'', } ...
  201.         # }
  202.         #
  203.         self.load()
  204.  
  205.     def load(self):
  206.         self._fab = os.path.join(prop.user_dir, "fab.pickle")
  207.         #old_fab = os.path.join(prop.user_dir, "fab.db")
  208.  
  209.         # Load the existing pickle if present
  210.         if os.path.exists(self._fab):
  211.             pickle_file = open(self._fab, "r")
  212.             self._data = cPickle.load(pickle_file)
  213.             pickle_file.close()
  214.  
  215.         else:
  216.             self.save() # save the empty file to create the file
  217.  
  218.  
  219.     def set(self, name, title, firstname, lastname, fax, groups, notes):
  220.         try:
  221.             grps = [unicode(s) for s in groups]
  222.         except UnicodeDecodeError:
  223.             grps = [unicode(s.decode('utf-8')) for s in groups]
  224.  
  225.         self._data[unicode(name)] = {'name' : unicode(name),
  226.                                     'title': unicode(title),  # NOT USED STARTING IN 2.8.9
  227.                                     'firstname': unicode(firstname), # NOT USED STARTING IN 2.8.9
  228.                                     'lastname': unicode(lastname), # NOT USED STARTING IN 2.8.9
  229.                                     'fax': unicode(fax),
  230.                                     'notes': unicode(notes),
  231.                                     'groups': grps}
  232.  
  233.         self.save()
  234.  
  235.     insert = set
  236.  
  237.  
  238.     def set_key_value(self, name, key, value):
  239.         self._data[unicode(name)][key] = value
  240.         self.save()
  241.  
  242.  
  243.     def get(self, name):
  244.         return self._data.get(name, None)
  245.  
  246.     select = get
  247.  
  248.     def rename(self, old_name, new_name):
  249.         try:
  250.             self._data[old_name]
  251.         except KeyError:
  252.             return
  253.         else:
  254.             try:
  255.                 self._data[new_name]
  256.             except KeyError:
  257.                 self._data[new_name] = self._data[old_name].copy()
  258.                 self._data[new_name]['name'] = new_name
  259.                 del self._data[old_name]
  260.                 self.save()
  261.  
  262.  
  263.     def get_all_groups(self):
  264.         all_groups = []
  265.         for e, v in self._data.items():
  266.             for g in v['groups']:
  267.                 if g not in all_groups:
  268.                     all_groups.append(g)
  269.         return all_groups
  270.  
  271.  
  272.     def get_all_records(self):
  273.         return self._data
  274.  
  275.  
  276.     def get_all_names(self):
  277.         return self._data.keys()
  278.  
  279.  
  280.     def save(self):
  281.         try:
  282.             pickle_file = open(self._fab, "w")
  283.             cPickle.dump(self._data, pickle_file, cPickle.HIGHEST_PROTOCOL)
  284.             pickle_file.close()
  285.         except IOError:
  286.             log.error("I/O error saving fab file.")
  287.  
  288.  
  289.     def clear(self):
  290.         self._data = {}
  291.         self.save()
  292.  
  293.  
  294.     def delete(self, name):
  295.         if name in self._data:
  296.             del self._data[name]
  297.             self.save()
  298.             return True
  299.  
  300.         return False
  301.  
  302.  
  303.     def last_modification_time(self):
  304.         try:
  305.             return os.stat(self._fab).st_mtime
  306.         except OSError:
  307.             return 0
  308.  
  309.  
  310.     def update_groups(self, group, members):
  311.         for e, v in self._data.items():
  312.             if v['name'] in members: # membership indicated
  313.                 if not group in v['groups']:
  314.                     v['groups'].append(unicode(group))
  315.             else:
  316.                 if group in v['groups']:
  317.                     v['groups'].remove(unicode(group))
  318.         self.save()
  319.  
  320.  
  321.     def delete_group(self, group):
  322.         for e, v in self._data.items():
  323.             if group in v['groups']:
  324.                 v['groups'].remove(unicode(group))
  325.         self.save()
  326.  
  327.  
  328.     def group_members(self, group):
  329.         members = []
  330.         for e, v in self._data.items():
  331.             if group in v['groups']:
  332.                 members.append(e)
  333.         return members
  334.  
  335.  
  336.     def add_to_group(self, group, members):
  337.         group_members = self.group_members(group)
  338.         new_group_members = []
  339.         for m in members:
  340.             if m not in group_members:
  341.                 new_group_members.append(m)
  342.  
  343.         self.update_groups(group, group_members + new_group_members)
  344.  
  345.  
  346.     def remove_from_group(self, group, remove_members):
  347.         group_members = self.group_members(group)
  348.         new_group_members = []
  349.         for m in group_members:
  350.             if m not in remove_members:
  351.                 new_group_members.append(m)
  352.  
  353.         self.update_groups(group, new_group_members)
  354.  
  355.  
  356.     def rename_group(self, old_group, new_group):
  357.         members = self.group_members(old_group)
  358.         self.update_groups(old_group, [])
  359.         self.update_groups(new_group, members)
  360.  
  361.  
  362.     def import_ldif(self, filename):
  363.         try:
  364.             data = open(filename, 'r').read()
  365.             log.debug_block(filename, data)
  366.             parser = FaxLDIFParser(open(filename, 'r'), self)
  367.             parser.parse()
  368.             self.save()
  369.             return True, ''
  370.         except ValueError, e:
  371.             return False, e.message
  372.  
  373.  
  374.     def import_vcard(self, filename):
  375.         data = file(filename, 'r').read()
  376.         log.debug_block(filename, data)
  377.  
  378.         for card in vcard.VCards(vcard.VFile(vcard.opentextfile(filename))):
  379.             log.debug(card)
  380.  
  381.             if card['name']:
  382.                 fax = ''
  383.                 for x in range(1, 9999):
  384.                     if x == 1:
  385.                         s = 'phone'
  386.                     else:
  387.                         s = 'phone%d' % x
  388.  
  389.                     try:
  390.                         card[s]
  391.                     except KeyError:
  392.                         break
  393.                     else:
  394.                         if 'fax' in card[s]['type']:
  395.                             fax = card[s]['number']
  396.                             break
  397.  
  398.                 org = card.get('organisation', '')
  399.                 if org:
  400.                     org = [org]
  401.                 else:
  402.                     org = card.get('categories', '').split(';')
  403.                     if not org:
  404.                         org = []
  405.  
  406.                 org.append(u'All')
  407.                 groups = [o for o in org if o]
  408.  
  409.                 name = card['name']
  410.                 notes = card.get('notes', u'')
  411.                 log.debug("Import: name=%s, fax=%s group(s)=%s notes=%s" % (name, fax, ','.join(groups), notes))
  412.                 self.set(name, u'', u'', u'', fax, groups, notes)
  413.  
  414.         return True, ''
  415.  
  416.  
  417. # **************************************************************************** #
  418. class FaxDevice(device.Device):
  419.  
  420.     def __init__(self, device_uri=None, printer_name=None,
  421.                  callback=None,
  422.                  fax_type=FAX_TYPE_NONE,
  423.                  disable_dbus=False):
  424.  
  425.         device.Device.__init__(self, device_uri, printer_name,
  426.                                None, callback, disable_dbus)
  427.  
  428.         self.send_fax_thread = None
  429.         self.upload_log_thread = None
  430.         self.fax_type = fax_type
  431.  
  432.         if not disable_dbus:
  433.             session_bus = dbus.SessionBus()
  434.             self.service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")
  435.         else:
  436.             self.service = None
  437.  
  438.  
  439.     def setPhoneNum(self, num):
  440.         raise AttributeError
  441.  
  442.     def getPhoneNum(self):
  443.         raise AttributeError
  444.  
  445.     phone_num = property(getPhoneNum, setPhoneNum)
  446.  
  447.  
  448.     def setStationName(self, name):
  449.         raise AttributeError
  450.  
  451.     def getStationName(self):
  452.         raise AttributeError
  453.  
  454.     station_name = property(getStationName, setStationName)
  455.  
  456.     def setDateAndTime(self):
  457.         raise AttributeError
  458.  
  459.     def uploadLog(self):
  460.         raise AttributeError
  461.  
  462.     def isUploadLogActive(self):
  463.         raise AttributeError
  464.  
  465.     def waitForUploadLogThread(self):
  466.         raise AttributeError
  467.  
  468.     def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
  469.                   cover_func=None, preserve_formatting=False, printer_name='',
  470.                   update_queue=None, event_queue=None):
  471.  
  472.         raise AttributeError
  473.  
  474.     def isSendFaxActive(self):
  475.         if self.send_fax_thread is not None:
  476.             return self.send_fax_thread.isAlive()
  477.         else:
  478.             return False
  479.  
  480.     def waitForSendFaxThread(self):
  481.         if self.send_fax_thread is not None and \
  482.             self.send_fax_thread.isAlive():
  483.  
  484.             try:
  485.                 self.send_fax_thread.join()
  486.             except KeyboardInterrupt:
  487.                 pass
  488.  
  489.  
  490. # **************************************************************************** #
  491.  
  492.  
  493. def getFaxDevice(device_uri=None, printer_name=None,
  494.                  callback=None,
  495.                  fax_type=FAX_TYPE_NONE,
  496.                  disable_dbus=False):
  497.  
  498.     if fax_type == FAX_TYPE_NONE:
  499.         if device_uri is None and printer_name is not None:
  500.             printers = cups.getPrinters()
  501.  
  502.             for p in printers:
  503.                 if p.name.lower() == printer_name.lower():
  504.                     device_uri = p.device_uri
  505.                     break
  506.             else:
  507.                 raise Error(ERROR_DEVICE_NOT_FOUND)
  508.  
  509.         if device_uri is not None:
  510.             mq = device.queryModelByURI(device_uri)
  511.             fax_type = mq['fax-type']
  512.  
  513.     log.debug("fax-type=%d" % fax_type)
  514.  
  515.     if fax_type in (FAX_TYPE_BLACK_SEND_EARLY_OPEN, FAX_TYPE_BLACK_SEND_LATE_OPEN):
  516.         from pmlfax import PMLFaxDevice
  517.         return PMLFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
  518.  
  519.     elif fax_type == FAX_TYPE_SOAP:
  520.         from soapfax import SOAPFaxDevice
  521.         return SOAPFaxDevice(device_uri, printer_name, callback, fax_type, disable_dbus)
  522.  
  523.     else:
  524.         raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
  525.  
  526. # **************************************************************************** #
  527.  
  528.  
  529. # TODO: Define these in only 1 place!
  530. STATE_DONE = 0
  531. STATE_ABORTED = 10
  532. STATE_SUCCESS = 20
  533. STATE_BUSY = 25
  534. STATE_READ_SENDER_INFO = 30
  535. STATE_PRERENDER = 40
  536. STATE_COUNT_PAGES = 50
  537. STATE_NEXT_RECIPIENT = 60
  538. STATE_COVER_PAGE = 70
  539. STATE_SINGLE_FILE = 80
  540. STATE_MERGE_FILES = 90
  541. STATE_SINGLE_FILE = 100
  542. STATE_SEND_FAX = 110
  543. STATE_CLEANUP = 120
  544. STATE_ERROR = 130
  545.  
  546. class FaxSendThread(threading.Thread):
  547.     def __init__(self, dev, service, phone_num_list, fax_file_list,
  548.                  cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
  549.                  printer_name='', update_queue=None, event_queue=None):
  550.  
  551.         threading.Thread.__init__(self)
  552.  
  553.         self.dev = dev # device.Device
  554.         self.service = service # dbus proxy to status server object
  555.         self.phone_num_list = phone_num_list
  556.         self.fax_file_list = fax_file_list
  557.         self.update_queue = update_queue
  558.         self.event_queue = event_queue
  559.         self.cover_message = cover_message
  560.         self.cover_re = cover_re
  561.         self.cover_func = cover_func
  562.         self.current_printer = printer_name
  563.         self.stream = StringIO()
  564.         self.prev_update = ''
  565.         self.remove_temp_file = False
  566.         self.preserve_formatting = preserve_formatting
  567.         self.results = {} # {'file' : error_code,...}
  568.         self.cover_page_present = False
  569.         self.recipient_file_list = []
  570.         self.f = None # final file of fax data to send (pages merged)
  571.         self.job_hort_dpi = 0
  572.         self.job_hort_dpi = 0
  573.         self.job_vert_dpi = 0
  574.         self.job_page_size = 0
  575.         self.job_resolution = 0
  576.         self.job_encoding = 0
  577.  
  578.  
  579.     def pre_render(self, state):
  580.         # pre-render each page that needs rendering
  581.         # except for the cover page
  582.         self.cover_page_present = False
  583.         log.debug(self.fax_file_list)
  584.  
  585.         for fax_file in self.fax_file_list: # (file, type, desc, title)
  586.             fax_file_name, fax_file_type, fax_file_desc, \
  587.                 fax_file_title, fax_file_pages = fax_file
  588.  
  589.             if fax_file_type == "application/hplip-fax-coverpage": # render later
  590.                 self.cover_page_present = True
  591.                 log.debug("Skipping coverpage")
  592.  
  593.             #if fax_file_type == "application/hplip-fax": # already rendered
  594.             else:
  595.                 self.rendered_file_list.append((fax_file_name, "application/hplip-fax",
  596.                     "HP Fax", fax_file_title))
  597.  
  598.                 log.debug("Processing pre-rendered file: %s (%d pages)" %
  599.                     (fax_file_name, fax_file_pages))
  600.  
  601.             if self.check_for_cancel():
  602.                 state = STATE_ABORTED
  603.  
  604.         log.debug(self.rendered_file_list)
  605.  
  606.         if self.check_for_cancel():
  607.             state = STATE_ABORTED
  608.  
  609.         return state
  610.  
  611.  
  612.     def count_pages(self, state):
  613.         self.recipient_file_list = self.rendered_file_list[:]
  614.         log.debug("Counting total pages...")
  615.         self.job_total_pages = 0
  616.         log.debug(self.recipient_file_list)
  617.  
  618.         i = 0
  619.         for fax_file in self.recipient_file_list: # (file, type, desc, title)
  620.             fax_file_name = fax_file[0]
  621.             log.debug("Processing file (counting pages): %s..." % fax_file_name)
  622.  
  623.             #self.write_queue((STATUS_PROCESSING_FILES, self.job_total_pages, ''))
  624.  
  625.             if os.path.exists(fax_file_name):
  626.                 self.results[fax_file_name] = ERROR_SUCCESS
  627.                 fax_file_fd = file(fax_file_name, 'r')
  628.                 header = fax_file_fd.read(FILE_HEADER_SIZE)
  629.  
  630.                 magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  631.                     resolution, encoding, reserved1, reserved2 = \
  632.                         self.decode_fax_header(header)
  633.  
  634.                 if magic != 'hplip_g3':
  635.                     log.error("Invalid file header. Bad magic.")
  636.                     self.results[fax_file_name] = ERROR_FAX_INVALID_FAX_FILE
  637.                     state = STATE_ERROR
  638.                     continue
  639.  
  640.                 if not i:
  641.                     self.job_hort_dpi, self.job_vert_dpi, self.job_page_size, \
  642.                         self.job_resolution, self.job_encoding = \
  643.                         hort_dpi, vert_dpi, page_size, resolution, encoding
  644.  
  645.                     i += 1
  646.                 else:
  647.                     if self.job_hort_dpi != hort_dpi or \
  648.                         self.job_vert_dpi != vert_dpi or \
  649.                         self.job_page_size != page_size or \
  650.                         self.job_resolution != resolution or \
  651.                         self.job_encoding != encoding:
  652.  
  653.                         log.error("Incompatible options for file: %s" % fax_file_name)
  654.                         self.results[fax_file_name] = ERROR_FAX_INCOMPATIBLE_OPTIONS
  655.                         state = STATE_ERROR
  656.  
  657.  
  658.                 log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  659.                           (magic, version, total_pages, hort_dpi,
  660.                            vert_dpi, page_size, resolution, encoding))
  661.  
  662.                 self.job_total_pages += total_pages
  663.  
  664.                 fax_file_fd.close()
  665.  
  666.             else:
  667.                 log.error("Unable to find HP Fax file: %s" % fax_file_name)
  668.                 self.results[fax_file_name] = ERROR_FAX_FILE_NOT_FOUND
  669.                 state = STATE_ERROR
  670.                 break
  671.  
  672.             if self.check_for_cancel():
  673.                 state = STATE_ABORTED
  674.                 break
  675.  
  676.  
  677.         if self.cover_page_present:
  678.             self.job_total_pages += 1 # Cover pages are truncated to 1 page
  679.  
  680.         log.debug("Total fax pages=%d" % self.job_total_pages)
  681.  
  682.         return state
  683.  
  684.     def decode_fax_header(self, header):
  685.         try:
  686.             return struct.unpack(">8sBIHHBBBII", header)
  687.         except struct.error:
  688.             return -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
  689.  
  690.     def decode_page_header(self, header):
  691.         try:
  692.             return struct.unpack(">IIIIII", header)
  693.         except struct.error:
  694.             return -1, -1, -1, -1, -1, -1
  695.  
  696.     def cover_page(self,  recipient):
  697.         if self.job_total_pages > 1:
  698.             state = STATE_MERGE_FILES
  699.         else:
  700.             state = STATE_SINGLE_FILE
  701.  
  702.         if self.cover_page_present:
  703.             log.debug("Creating cover page for recipient: %s" % recipient['name'])
  704.             fax_file, canceled = self.render_cover_page(recipient)
  705.  
  706.             if canceled:
  707.                 state = STATE_ABORTED
  708.             elif not fax_file:
  709.                 state = STATE_ERROR # timeout
  710.             else:
  711.                 self.recipient_file_list.insert(0, (fax_file, "application/hplip-fax",
  712.                                                     "HP Fax", 'Cover Page'))
  713.  
  714.                 log.debug("Cover page G3 file: %s" % fax_file)
  715.  
  716.                 self.results[fax_file] = ERROR_SUCCESS
  717.  
  718.         return state
  719.  
  720.     def single_file(self, state):
  721.         state = STATE_SEND_FAX
  722.  
  723.         log.debug("Processing single file...")
  724.         self.f = self.recipient_file_list[0][0]
  725.  
  726.         try:
  727.             f_fd = file(self.f, 'r')
  728.         except IOError:
  729.             log.error("Unable to open fax file: %s" % self.f)
  730.             state = STATE_ERROR
  731.         else:
  732.             header = f_fd.read(FILE_HEADER_SIZE)
  733.  
  734.             magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  735.                 resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
  736.  
  737.             self.results[self.f] = ERROR_SUCCESS
  738.  
  739.             if magic != 'hplip_g3':
  740.                 log.error("Invalid file header. Bad magic.")
  741.                 self.results[self.f] = ERROR_FAX_INVALID_FAX_FILE
  742.                 state = STATE_ERROR
  743.  
  744.             log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  745.                       (magic, version, total_pages, hort_dpi, vert_dpi,
  746.                        page_size, resolution, encoding))
  747.  
  748.             f_fd.close()
  749.  
  750.         return state
  751.  
  752.  
  753.     def merge_files(self, state):
  754.         log.debug("%s State: Merge multiple files" % ("*"*20))
  755.         log.debug(self.recipient_file_list)
  756.         log.debug("Merging g3 files...")
  757.         self.remove_temp_file = True
  758.  
  759.         if self.job_total_pages:
  760.             f_fd, self.f = utils.make_temp_file()
  761.             log.debug("Temp file=%s" % self.f)
  762.  
  763.             data = struct.pack(">8sBIHHBBBII", "hplip_g3", 1L, self.job_total_pages,
  764.                 self.job_hort_dpi, self.job_vert_dpi, self.job_page_size,
  765.                 self.job_resolution, self.job_encoding,
  766.                 0L, 0L)
  767.  
  768.             os.write(f_fd, data)
  769.  
  770.             job_page_num = 1
  771.  
  772.             for fax_file in self.recipient_file_list:
  773.                 fax_file_name = fax_file[0]
  774.                 log.debug("Processing file: %s..." % fax_file_name)
  775.  
  776.                 if self.results[fax_file_name] == ERROR_SUCCESS:
  777.                     fax_file_fd = file(fax_file_name, 'r')
  778.                     header = fax_file_fd.read(FILE_HEADER_SIZE)
  779.  
  780.                     magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  781.                         resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
  782.  
  783.                     if magic != 'hplip_g3':
  784.                         log.error("Invalid file header. Bad magic.")
  785.                         state = STATE_ERROR
  786.                         break
  787.  
  788.                     log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  789.                               (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  790.  
  791.                     for p in range(total_pages):
  792.                         header = fax_file_fd.read(PAGE_HEADER_SIZE)
  793.  
  794.                         page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
  795.                             self.decode_page_header(header)
  796.  
  797.                         if page_num == -1:
  798.                             log.error("Page header error")
  799.                             state - STATE_ERROR
  800.                             break
  801.  
  802.                         header = struct.pack(">IIIIII", job_page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, 0L)
  803.                         os.write(f_fd, header)
  804.  
  805.                         self.write_queue((STATUS_PROCESSING_FILES, job_page_num, ''))
  806.  
  807.                         log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%s" %
  808.                                   (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
  809.  
  810.                         os.write(f_fd, fax_file_fd.read(bytes_to_read))
  811.                         job_page_num += 1
  812.  
  813.                     fax_file_fd.close()
  814.  
  815.                     if self.check_for_cancel():
  816.                         state = STATE_ABORTED
  817.                         break
  818.  
  819.                 else:
  820.                     log.error("Skipping file: %s" % fax_file_name)
  821.                     continue
  822.  
  823.             os.close(f_fd)
  824.             log.debug("Total pages=%d" % self.job_total_pages)
  825.  
  826.         return state
  827.  
  828.  
  829.     def next_recipient_gen(self):
  830.         for a in self.phone_num_list:
  831.             yield a
  832.  
  833.  
  834.     def render_file(self, path, title, mime_type, force_single_page=False):
  835.         all_pages = True
  836.         page_range = ''
  837.         page_set = 0
  838.         nup = 1
  839.  
  840.         cups.resetOptions()
  841.  
  842.         if mime_type in ["application/x-cshell",
  843.                          "application/x-perl",
  844.                          "application/x-python",
  845.                          "application/x-shell",
  846.                          "application/x-sh",
  847.                          "text/plain",]:
  848.  
  849.             cups.addOption('prettyprint')
  850.  
  851.         if nup > 1:
  852.             cups.addOption('number-up=%d' % nup)
  853.  
  854.         if force_single_page:
  855.             cups.addOption('page-ranges=1') # Force coverpage to 1 page
  856.  
  857.         sent_job_id = cups.printFile(self.current_printer, path, title)
  858.         cups.resetOptions()
  859.  
  860.         log.debug("Job ID=%d" % sent_job_id)
  861.         job_id = 0
  862.  
  863.         time.sleep(1)
  864.  
  865.         fax_file = ''
  866.         complete = False
  867.  
  868.         end_time = time.time() + 300.0 # wait for 5 min. max
  869.         while time.time() < end_time:
  870.             log.debug("Waiting for fax...")
  871.  
  872.             result = list(self.service.CheckForWaitingFax(self.dev.device_uri, prop.username, sent_job_id))
  873.  
  874.             fax_file = str(result[7])
  875.             log.debug("Fax file=%s" % fax_file)
  876.  
  877.             if fax_file:
  878.                 break
  879.  
  880.             if self.check_for_cancel():
  881.                 log.error("Render canceled. Canceling job #%d..." % sent_job_id)
  882.                 cups.cancelJob(sent_job_id)
  883.                 return '', True
  884.  
  885.             time.sleep(1)
  886.  
  887.         else:
  888.             log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
  889.             cups.cancelJob(sent_job_id)
  890.             return '', False
  891.  
  892.         return fax_file, False
  893.  
  894.  
  895.     def check_for_cancel(self):
  896.         canceled = False
  897.         while self.event_queue.qsize():
  898.             try:
  899.                 event = self.event_queue.get(0)
  900.                 if event[0] == EVENT_FAX_SEND_CANCELED:
  901.                     canceled = True
  902.                     log.debug("Cancel pressed!")
  903.             except Queue.Empty:
  904.                 break
  905.  
  906.         return canceled
  907.  
  908.     def render_cover_page(self, a):
  909.         log.debug("Creating cover page...")
  910.  
  911.         pdf = self.cover_func(page_size=coverpages.PAGE_SIZE_LETTER,
  912.                               total_pages=self.job_total_pages,
  913.  
  914.                               recipient_name=a['name'],
  915.                               recipient_phone='', # ???
  916.                               recipient_fax=a['fax'],
  917.  
  918.                               sender_name=self.sender_name,
  919.                               sender_phone=user_conf.get('fax', 'voice_phone'),
  920.                               sender_fax=self.sender_fax,
  921.                               sender_email=user_conf.get('fax', 'email_address'),
  922.  
  923.                               regarding=self.cover_re,
  924.                               message=self.cover_message,
  925.                               preserve_formatting=self.preserve_formatting)
  926.  
  927.         log.debug("PDF File=%s" % pdf)
  928.         fax_file, canceled = self.render_file(pdf, 'Cover Page', "application/pdf",
  929.             force_single_page=True)
  930.  
  931.         try:
  932.             os.remove(pdf)
  933.         except IOError:
  934.             pass
  935.  
  936.         return fax_file, canceled
  937.  
  938.  
  939.     def write_queue(self, message):
  940.         if self.update_queue is not None and message != self.prev_update:
  941.             self.update_queue.put(message)
  942.             time.sleep(0)
  943.             self.prev_update = message
  944.  
  945.  
  946.     def run(self):
  947.         pass
  948.  
  949.  
  950.  
  951.